package utils

import (
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"fmt"
	"io/ioutil"

	peercrypto "github.com/libp2p/go-libp2p-core/crypto"
	"github.com/libp2p/go-libp2p-core/peer"
	"github.com/multiformats/go-multiaddr"
)

//IDFromPublicKey 公钥转ID
func IDFromPublicKey(path string) (peer.ID, error) {
	pubKey, err := GetPeerIDPublicKey(path)
	if err != nil {
		return "", err
	}
	return peer.IDFromPublicKey(pubKey)
}

//IDFromPrivateKey 私钥转ID
func IDFromPrivateKey(path string) (peer.ID, error) {
	//解析私钥
	priKey, err := GetPeerIDPrivateKey(path)
	if err != nil {
		return "", err
	}
	return peer.IDFromPrivateKey(priKey)
}

//GetPeerIDPublicKey 文件中获取公钥
func GetPeerIDPublicKey(path string) (peercrypto.PubKey, error) {
	publicKeyFile, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	str := string(publicKeyFile)
	block, _ := pem.Decode([]byte(str))
	if block == nil {
		return nil, errors.New("decode public key failed")
	}
	return peercrypto.UnmarshalRsaPublicKey(block.Bytes)
}

//GetPeerIDPrivateKey 文件中获取私钥
func GetPeerIDPrivateKey(path string) (peercrypto.PrivKey, error) {
	//解析私钥
	if path == "" {
		priv, _, err := peercrypto.GenerateKeyPair(peercrypto.RSA, 2048)
		return priv, err
	}
	privateKeyFile, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	str := string(privateKeyFile)
	block, _ := pem.Decode([]byte(str))
	if block == nil {
		return nil, errors.New("decode private key failed")
	}
	key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		return nil, fmt.Errorf("parse private key failed, err: %s", err.Error())
	}
	priKey := key.(*rsa.PrivateKey)
	buf := x509.MarshalPKCS1PrivateKey(priKey)

	return peercrypto.UnmarshalRsaPrivateKey(buf)
}

//PeerIDEncode peerID编码
func PeerIDEncode(id peer.ID) string {
	return id.String()
}

//PeerIDDecode peerID解码
func PeerIDDecode(id string) (peer.ID, error) {
	return peer.Decode(id)
}

//GenAddrInfo 获取peerInfo
func GenAddrInfo(addr string) (*peer.AddrInfo, error) {
	mAddr, err := multiaddr.NewMultiaddr(addr)
	if err != nil {
		return nil, err
	}
	peerInfo, err := peer.AddrInfoFromP2pAddr(mAddr)
	if err != nil {
		return nil, err
	}
	return peerInfo, err
}
